﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SKYPE4COMLib;
using System.Threading;
using System.Runtime.InteropServices;

namespace SkypeApp
{
    public class SkypeService
    {
        #region private instance state
        ISkype Skype
        {
            get;
            set;
        }

        string StartVideoForHandler
        {
            get;
            set;
        }

        #endregion

        #region public wrapper API

        public event EventHandler OffLine;
        public event EventHandler RefreshUsers;
        public event EventHandler CallFinished;

        public bool AutoAddContacts
        {
            get;
            set;
        }

        public SkypeService()
        {
            Skype = new Skype("Skype4COMLib.Skype", "Skype_");
            ((Skype)Skype).CallStatus += new _ISkypeEvents_CallStatusEventHandler(SkypeService_CallStatus);
            ((Skype)Skype).UserStatus += new _ISkypeEvents_UserStatusEventHandler(SkypeController_UserStatus);
            ((Skype)Skype).OnlineStatus += new _ISkypeEvents_OnlineStatusEventHandler(SkypeService_OnlineStatus);
            ((Skype)Skype).MessageStatus += new _ISkypeEvents_MessageStatusEventHandler(SkypeService_MessageStatus);
        }

        public IUser GetFriend(string handle)
        {
            if (!EnsureConnected())
                return null;

            foreach (IUser friend in Skype.Friends)
            {
                if (friend.Handle == handle)
                    return friend;
            }
            return null;
        }

        public bool TryStartVideoSend(Call pCall)
        {
            if (!EnsureConnected())
                return false;
            try
            {
                pCall.StartVideoSend();
                FocusSkypeWindow();
                return true;
            }
            catch (Exception e)
            {
                return false;
            }
        }

        public bool EnsureConnected()
        {
            try
            {
                EnsureSkypeRunning();
                EnsureAttached();
                if (!IsOnline)
                {
                    RaiseOffline();
                    return false;
                }
                return true;
            }
            catch (COMException)
            {
                RaiseOffline();
                return false;
            }
        }

        public bool HasActiveCalls()
        {
            if (!EnsureConnected())
                return false;

            return Skype.ActiveCalls != null && Skype.ActiveCalls.Count > 0;
        }

        public void TryFinishCall()
        {
            if (HasActiveCalls())
            {
                ICall call = Skype.ActiveCalls[1];
                if (call != null)
                    call.Finish();
            }
        }

        /// <summary>
        /// Tries the partner ansver.
        /// </summary>
        /// <returns>The partner handle or <c>null</c> if fail.</returns>
        public string TryPartnerAnsver()
        {
            if (!HasActiveCalls())
                return null;

            ICall call = Skype.ActiveCalls[1];

            if (call.Status == TCallStatus.clsRinging)
            {
                if (call.Type == TCallType.cltIncomingP2P || call.Type == TCallType.cltIncomingPSTN)
                {
                    StartVideoForHandler = call.PartnerHandle;
                    call.Answer();
                    return call.PartnerHandle;
                }
            }

            return null;
        }

        public void FocusSkypeWindow()
        {
            if (EnsureConnected())
            {
                Skype.Client.Focus();
            }
        }

        public void HideSkypeWindow()
        {
            if (EnsureConnected())
            {
                Skype.Client.Minimize();
            }
        }

        public ICall PlaceCall(IUser friend)
        {
            if (!EnsureConnected())
            {
                StartVideoForHandler = null;
                return null;
            }

            StartVideoForHandler = friend.IsVideoCapable ? friend.Handle : null;
            return Skype.PlaceCall(friend.Handle);
        }

        public List<IUser> GetFriends()
        {
            if (!EnsureConnected())
                return new List<IUser>();

            List<IUser> friends = new List<IUser>();
            foreach (IUser friend in Skype.Friends)
                friends.Add(friend);

            return friends;
        }

        #endregion

        #region private stuff

        private bool IsOnline
        {
            get { return Skype.ConnectionStatus == TConnectionStatus.conOnline; }
        }

        #region event handlers

        void SkypeService_CallStatus(Call pCall, TCallStatus Status)
        {
            if (Status == TCallStatus.clsInProgress && pCall.PartnerHandle == StartVideoForHandler)
            {
                TryStartVideoSend(pCall);
                StartVideoForHandler = null;
            }
            else
                if (Status == TCallStatus.clsFinished || Status == TCallStatus.clsFailed || Status == TCallStatus.clsCancelled)
                    RaiseCallFinished();
        }

        void SkypeService_OnlineStatus(User pUser, TOnlineStatus Status)
        {
            RaiseRefreshUsers();
        }

        void SkypeController_UserStatus(TUserStatus Status)
        {
            if (Status == TUserStatus.cusLoggedOut || Status == TUserStatus.cusOffline)
                RaiseOffline();
            else
                RaiseRefreshUsers();
        }

        void SkypeService_MessageStatus(ChatMessage pMessage, TChatMessageStatus status)
        {
            if (status == TChatMessageStatus.cmsReceived)
            {
                if (AutoAddContacts
                    && GetFriend(pMessage.FromHandle) != null
                    && !string.IsNullOrEmpty(pMessage.Body))
                    ProcessMessage(pMessage.Body);
            }
        }

        #endregion

        private void ProcessMessage(string text)
        {
            if (text.ToLower().StartsWith("add:"))
                AddFriend(text.Remove(0, 4));
        }


        private void RaiseRefreshUsers()
        {
            if (RefreshUsers != null)
                RefreshUsers(this, EventArgs.Empty);
        }

        private void RaiseOffline()
        {
            if (OffLine != null)
                OffLine(this, EventArgs.Empty);
        }

        private void RaiseCallFinished()
        {
            if (CallFinished != null)
                CallFinished(this, EventArgs.Empty);
        }

        private void EnsureSkypeRunning()
        {
            if (!Skype.Client.IsRunning)
            {
                Skype.Client.Start(true, true);
                while (!Skype.Client.IsRunning)
                {
                    Thread.Sleep(100);
                }
            }
        }

        private void EnsureAttached()
        {
            if (Skype.AttachmentStatus != TAttachmentStatus.apiAttachSuccess)
            {
                if (Skype.AttachmentStatus == TAttachmentStatus.apiAttachPendingAuthorization)
                {
                    while (Skype.AttachmentStatus == TAttachmentStatus.apiAttachPendingAuthorization)
                    {
                        Thread.Sleep(100);
                    }
                    if (Skype.AttachmentStatus == TAttachmentStatus.apiAttachSuccess)
                        return;
                }

                Skype.Attach(8, true);
            }
        }

        private void AddFriend(string name)
        {
            IUserCollection users = Skype.SearchForUsers(name);
            User userToAdd = null;
            int count = 0;
            foreach (User user in users)
                if (user.Handle == name)
                {
                    userToAdd = user;
                    count++;
                }

            if (userToAdd != null && count == 1
                && (userToAdd.BuddyStatus == TBuddyStatus.budNeverBeenFriend
                || userToAdd.BuddyStatus == TBuddyStatus.budDeletedFriend))
            {
                userToAdd.BuddyStatus = TBuddyStatus.budPendingAuthorization;
            }
        }

        #endregion
    }
}
